# 正则表达式
"""
1、正则表达式的定义：
正则表达式是对字符串操作的一种逻辑公式，就是用事先定义好的一些特殊字符、及这些特定字符的组合，组成一个”规则字符串“，这个”规则字符串“用来表达对字符串的一种过滤逻辑。

正则表达式是对字符串（包括普通字符（类如a-z之间的字母）和特殊字符（成为”元字符“））操作的一种逻辑公式，就是用事先定义好的一些特定字符、及这些特定字符的组合，组成一
个”规则字符串“，这个“规则字符串”用来表达对字符串的一种过滤逻辑。正则表达式是一种文本模式，模式描述在搜索文本时要匹配一个或者多个字符串。

正则表达式，又称正则表示式、正规表示式、规则表达式、常规表示法（英文：Regular Expression，在代码中regex、regexp或RE）是计算机科学一种概念。正则表达式使用单个
字符串来描述、匹配一系列匹配某个句法规则的字符串。在很多文本编辑器里，正则表达式通常被用来检索、替换哪些匹配某个模式的文本。

Regular Expression ”Regular“ 一般被翻译为”正则“、”正规“、”常规“。此处的”Regular“即是”规则“、”规律”的意思 Regular Expression即“描述某种规则的表达式”之意

正则在所有语言中都有的内容

2、正则表达式的作用和特点
给定一个正则表达式和一个字符串，可以达到如下目的
1）给定的字符串是否符合正则表达式的过滤逻辑（成为“匹配”）
2) 可以通过正则表达式，从字符串中获取我们想要的特定部分

正则表达式的特点：
1)灵活性、逻辑性和功能性非常强
2）可以迅速用极简单的方法达到字符串的复杂控制
3)对于刚接触的人比较晦涩难懂

场景：
如何判断一个字符串是手机号?

判断邮箱为163或者126的所有邮件地址
假如你在写一个爬虫，你得到了一个网页的HTML源码，其中有一段

s = "<html><body><h1>hello world</h1></body></html>"
你想要把这个hello world 提取出来，但你这时如果只会python 的字符串处理，那么第一反应可能是
s = "<html><body><h1>hello world</h1></body></html>"
start_index = s.find('<h1>')

python  re模块

\A:表示从字符串的开始处匹配
\Z: 表示从字符串的结束处匹配，如果存在换行，只匹配到换行前的结束字符串
\b: 匹配一个单词边界，也就是指单词和空格间的位置。例如，‘py\b’ 可以匹配”python“中的”py“，但不能匹配”openpyxl“中的”py“
\B: 匹配非单词边界。”py\b“ 可以匹配”openpyxl“ 中的”py“， 但不能匹配”python“ 中的”py“。
\d:匹配任意数字，等价于[0-9].             digit
\D:匹配任意非数字字符，等价于[^\d]         not  digit
\s:匹配任意空白字符，等价于[\t\n\r\f].     space
\S:匹配任意非空白字符，等价于【^\s】.
\w:匹配任意字母数字及下划线，等价于[a-zA-Z0-9_]     word
\W:匹配任意非字母数字及下划线，等价于[^\w]
\\:匹配原意的反斜杠\。

".":用于匹配换行符(\n)之外的字符。  (任意字符除了\n) 注意： . 必须被转义否则会匹配任意字符  建议\.
"^":方括号里边是非； 用于匹配字符串的开始
"$":用于匹配字符串的末尾(末尾如果有换行符\n,就匹配\n前面的那个字符)，即行尾
"*" 用于将前面的模式匹配0此或多次（贪婪模式：即尽可能多匹配）
"+" 用于将前面的模式匹配1此或者多次（贪婪模式）
"?" 用于将前面的模式匹配0此或者1次（贪婪模式）
"*?,+?,??" 即上面三种特殊字符的非贪婪模式（尽可能少的匹配）
“{m,n}” 用于将前面的模式匹配m次到n次（贪婪模式），即最小匹配m次，最大匹配n次。
“{m,n}?” 即上面“{m,n}”的非贪婪版本
\\: '\'是转义字符，在特殊字符前面加\,特殊字符就失去了其所代表的含义，比如\+就仅仅代表加号+本身
[]:用于标识一组字符，如果^是第一个字符，则标示的是一个补集。比如[0-9]标示所有的数字，[^0-9]标识除了数字外的字符。
|:比如A|B用于匹配A或者B
(...):用于匹配括号中的模式，可以在字符串中检索或匹配我们所需要的内容。

[] :表示范围  [abc][a-z] [a-z*&]或者的关系   、表示单个字母而不是但单词
|：或者
()：#  (qq|163|126)  小括号表示整体返回的或者  qq 或者163 或者126


量词：
*   >=0
+   >=1
？   0,1
{m} 固定几位
{m,}    >=m
{m,n}   m<= x <=n

正则符号：

注意：
 　\d  [a-z]  指标是一个字符
   . 必须被转义否则会匹配任意字符  建议\.

Python里数量词默认是贪婪模式（在少数语言里也可能是默认非贪婪），总是尝试匹配尽可能多的字符；
非贪婪则相反，总是尝试匹配尽可能少的字符。

在”*“，”？“，"+" {m,n}后面加？，使贪婪变成非贪婪
"""
# 使用正则表达式验证qq的合法性验证
qq = input("请输入你的QQ号码：")
if len(qq) >= 5 and not qq.startswith("0"):  # qq[0] !='0'
    print("qq号码是合法的")
else:
    print("不合法")

# 正则表达式

import re

message = 'sanduo lily lucy'
pattern = re.compile('lily')  # 匹配失败：none
# pattern = re.compile('sanduo') # 匹配成功：<re.Match object; span=(0, 6), match='sanduo'>
result = pattern.match(message)  # 没有匹配返回空（从头开始匹配）

print(result)

# 正则模块的方法re ： match

re_match = re.match("sanduo", message)  # 从头开始匹配，没有就返回None
print(re_match)  # <re.Match object; span=(0, 6), match='sanduo'>

re_search = re.search("lily", message)  # <re.Match object; span=(7, 11), match='lily'>  #匹配的是整个字符串（从头一直找到尾）
print(re_search)
print(re_search.span())  # (7, 11)  span 是匹配的位置；翻译一个位置
print(re_search.groups())  # () 空元组
print(re_search.group())  # 使用group提取匹配的数据

# a2b h6k

msg = 'bckshsaa cinlwn&nel*223jluin8819jhunk21nlini'

result = re.findall('[a-z][0-9]+[a-z]', msg)
print(result)  # ['n8819j', 'k21n']

# 用户名可以是字母或者数字，不能是数字开头，用户长度必须是6位以上['a-zA-Z\[d|a-zA-Z]{5,}']

name1 = '0Asanduo123'
name2 = "SanDUuo123"
name3 = "SanDUuo123#&"  # 必须用$符号结尾
name4 = "123SanDUuo123#&"  # 必须用^符号限制开始
name1_result = re.match("[a-zA-Z][0-9a-zA-Z]{5,}", name1)  # name1_result None
print("name1_result", name1_result)
name2_result = re.match("[a-zA-Z][0-9a-zA-Z]{5,}", name2)  # <re.Match object; span=(0, 10), match='1008001917'>
print("name2_result", name2_result)
name3_result = re.match("[a-zA-Z][0-9a-zA-Z]{5,}$", name3)  # match  $ 限定结尾；name3_result None
print("name3_result", name3_result)
name4_result = re.search("^[a-zA-Z][0-9a-zA-Z_]{5,}$", name4)  # 如果是search：^  限定开始  name4_result None
print("name4_result", name4_result)

name4_result = re.search("^[a-zA-Z]\w{5,}$", name4)  # 如果是search：^  限定开始  name4_result None
print("name4_result", name4_result)

# qq号码校验 5-11位 开头不能是0

qq = "1008001917"
qq_match = re.match("^[1-9][0-9]{4,10}$", qq)

print(qq_match)  # <re.Match object; span=(0, 10), match='1008001917'>

msg = "aa.py ab.txt bbb.py cc.py DD*py"

msg_border = re.findall("py\\b", msg)  # py\\b   等价  r"py\b"
print(msg_border)  # ['py', 'py', 'py', 'py']
msg_border = re.findall(r"\w*\.py\b", msg)  # ｒ不发生转义  . 必须被转义否则会匹配任意字符  建议\.
print(msg_border)  # ['aa.py', 'bbb.py', 'cc.py']

# 手机号码正则

phone = "18700191127"
match_none = re.match(r"^1[35789]\d{9}$", phone)
print("match_none", match_none)  # match_none <re.Match object; span=(0, 11), match='18700191127'>

print("贪婪非贪婪操作".center(20, "*"))

msg = "abc123adrd"
msg_match = re.match(r"[a-z]+\d+", msg)

# 贪婪模式：默认 只要后面有符合条件的，就往后面取
print(msg_match)  # <re.Match object; span=(0, 6), match='abc123'>

msg_match = re.match(r"[a-z]+\d+?", msg)

# 非贪婪模式：将贪婪模式变成非贪婪模式 加 “?”  只要发现就停止了，不会在给后面匹配了
print(msg_match)  # <re.Match object; span=(0, 4), match='abc1'>

path = '<img class="main_img img-hover" data-imgurl="http://t14.baidu.com/it/u=963524666,2960549705&amp;fm=224&amp;app=112&amp;f=JPEG?w=500&amp;h=500" src="http://t14.baidu.com/it/u=963524666,2960549705&amp;fm=224&amp;app=112&amp;f=JPEG?w=500&amp;h=500" style="width: 151px; height: 160px;">'

img_match = re.match(r'<img class="main_img img-hover" data-imgurl=(.+) src=(.+?)"', path)

print(img_match)
print(img_match.groups())
print(img_match.group(1))# http://t14.baidu.com/it/u=963524666,2960549705&amp;fm=224&amp;app=112&amp;f=JPEG?w=500&amp;h=500
print(img_match.group(2)) # http://t14.baidu.com/it/u=963524666,2960549705&amp;fm=224&amp;app=112&amp;f=JPEG?w=500&amp;h=500

# 链接有逗号报错：requests.exceptions.InvalidSchema: No connection adapters were found for
image_url = img_match.group(1)

import requests

response = requests.get("http://bpic.588ku.com/webpage_pic/22/06/29/b5a3b7ea8e66fa74090d7e1269a2b478.jpg")
# response = requests.get(image_url)  # requests.exceptions.InvalidSchema: No connection adapters were found for

print(response)
with open('aa.jpg', 'wb') as write_stream:
    # print(response)
    write_stream.write(response.content)
